package top.gytf.unifiedcall.common.detial;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import org.springframework.util.Assert;
import top.gytf.distools.lifecycle.AbstractLifecycle;
import top.gytf.distools.lifecycle.Manager;
import top.gytf.unifiedcall.model.Sortable;
import top.gytf.unifiedcall.model.detail.UnifiedCallPackageDetail;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通用的统一调用包管理实现
 */
public class UnifiedCallPackageManagerImpl extends AbstractLifecycle implements UnifiedCallPackageManager, Manager {
    private final List<UnifiedCallPackageLoader> loaders;

    private final Map<String, UnifiedCallPackageDetail> detailNameMapping = new ConcurrentHashMap<>();
    private final Map<Class<?>, UnifiedCallPackageDetail> detailClientMapping = new ConcurrentHashMap<>();
    private final Cache<Class<?>, UnifiedCallPackageDetail> nestedDetailClientFoundCache = CacheUtil.newLRUCache(256);

    public UnifiedCallPackageManagerImpl(Collection<UnifiedCallPackageLoader> loaders) {
        this.loaders = new ArrayList<>(loaders.stream()
                .sorted(Comparator.comparingInt(Sortable::order))
                .toList());
    }

    @Override
    protected void doInitialize() {
        loaders.forEach(UnifiedCallPackageLoader::initialize);
        for (UnifiedCallPackageLoader loader : loaders) {
            Set<UnifiedCallPackageDetail> detailSet = loader.load();
            detailSet.forEach(detail -> {
                Assert.isNull(detailNameMapping.put(detail.getName(), detail),
                        "Duplicate UnifiedCallPackageDetail found for name '" + detail.getName() + "'");
                detail.getClients().forEach(client -> Assert.isNull(detailClientMapping.put(client, detail),
                        "Duplicate UnifiedCallPackageDetail found for client '" + client + "'"));
            });
        }
    }

    @Override
    protected void doDestroy() {
        loaders.forEach(UnifiedCallPackageLoader::destroy);
    }

    @Override
    public Optional<UnifiedCallPackageDetail> findByName(String name) {
        return Optional.ofNullable(detailNameMapping.get(name));
    }

    @Override
    public Optional<UnifiedCallPackageDetail> findByClient(Class<?> client) {
        return Optional.ofNullable(nestedDetailClientFoundCache.get(client, true, () -> {
            for (Map.Entry<Class<?>, UnifiedCallPackageDetail> entry : detailClientMapping.entrySet()) {
                if (entry.getKey().isAssignableFrom(client)) {
                    return entry.getValue();
                }
            }
            return null;
        }));
    }
}
